home *** CD-ROM | disk | FTP | other *** search
/ Apple Developer Connection Student Program / ADC Tools Sampler CD Disk 3 1999.iso / Metrowerks CodeWarrior / Java Support / Java_Source / IFC_112 / netscape / application / Application.java < prev    next >
Encoding:
Text File  |  1999-05-28  |  38.3 KB  |  1,177 lines  |  [TEXT/CWIE]

  1. // Application.java
  2. // By Ned Etcode
  3. // Copyright 1995, 1996, 1997 Netscape Communications Corp.  All rights reserved.
  4.  
  5. package netscape.application;
  6.  
  7. import netscape.util.*;
  8.  
  9. import java.lang.Thread;
  10. import java.net.URL;
  11. import java.applet.AppletContext;
  12.  
  13. /** Object subclass that represents the overall IFC-based Java
  14.   * application. An Application instance maintains application-wide state and
  15.   * manages access to resources or objects that provide resources
  16.   * or services, such as RootViews.
  17.   * @note 1.0 New methods for Keyboard UI support
  18.   * @note 1.0 New methods for Menu support
  19.   * @note 1.0 Added internal methods so users can properly override will/did
  20.   *           process event.
  21.   * @note 1.0 Added support for JDK 1.1 Clipboard
  22.   * @note 1.0 Focus model changed because of Keyboard UI
  23.   * @note 1.0 added applet() method
  24.   * @note 1.0 added setClipboardText(), clipboardText() methods
  25.   * @note 1.1 Fixed isPaused()
  26.   * @note 1.1 drawAllDirtyViews to protected
  27.   * @note 1.1 beginModalSessionForView to protected
  28.   * @note 1.1 endModalSessionForView to protected
  29.   * @note 1.1 cleanup() does a better job now - Applications can be released now
  30.   * @note 1.1 sets application ivar in EventLoop for Timer support
  31.   * @note 1.1 Added setHandleJDK11MouseEvents() method.
  32.   * @note 1.1.1 added keyTyped method.
  33.   * @note 1.1.1 added handleExtendedKeyEvent method.
  34.   * @note 1.1.1 added classForName
  35.   */
  36.  
  37. public class Application implements Runnable, EventProcessor {
  38.     static Hashtable    groupToApplication = new Hashtable();
  39.     static final String _releaseName = "IFC 1.1.2";
  40.     static Clipboard    clipboard;
  41.     static Object       clipboardLock = new Object();
  42.  
  43.     java.applet.Applet    applet;
  44.     AppletResources     _appResources;
  45.     EventLoop           eventLoop = new EventLoop();
  46.     Vector              _languageVector;
  47.     Vector              rootViews = new Vector();
  48.     RootView            mainRootView;
  49.     boolean             didCreateApplet;
  50.     Vector              _modalVector = new Vector();
  51.     Vector              observers = new Vector();
  52.     Vector              activeMenuViews = new Vector();
  53.     boolean             jdkMouseEventHackEnabled = true;
  54.  
  55.     // This is application wide state kept on behalf of Bitmap, Sound, and
  56.     // Font. Because we keep a hashtable of images, they will never go away
  57.     // for the lifetime of an app. This code would get much nicer if each
  58.     // application got its own static variables and would get better still if
  59.     // the language had weak references.
  60.  
  61.     java.awt.MediaTracker tracker = null;
  62.     int                   bitmapCount = 0;
  63.     Hashtable             bitmapByName = new Hashtable();
  64.     Hashtable             soundByName = new Hashtable();
  65.     Hashtable             fontByName = new Hashtable();
  66.  
  67.     // This is information kept for graphics debugging
  68.     DebugGraphicsInfo   debugGraphicsInfo;
  69.  
  70.     // This is the shared TimerQueue for the Application.
  71.     TimerQueue timerQueue;
  72.  
  73.     // This lock is used to make the awt thread waiting for
  74.     // the application cleanup
  75.     Object cleanupLock;
  76.  
  77.     boolean isPaused;
  78.     boolean _kbdUIEnabled = true;
  79.  
  80.     Window  currentDocumentWindow; /* Contains the current document window internal or external */
  81.  
  82.     KeyboardArrow keyboardArrow;  /* Contains the internal window used to display the arrow */
  83.  
  84.  
  85.     /** Arrow position for the keyboard UI arrow */
  86.  
  87.     /** Arrow pointing to the selected view's top left corner
  88.       *
  89.       */
  90.     public static final int TOP_LEFT_POSITION = 0;
  91.  
  92.     /** Arrow pointing to the selected view's bottom left corner
  93.       *
  94.       */
  95.     public static final int BOTTOM_LEFT_POSITION  = 1;
  96.  
  97.     /** Arrow pointing to the selected view's top right corner
  98.       *
  99.       */
  100.     public static final int TOP_RIGHT_POSITION = 2;
  101.  
  102.     /** Arrow pointing to the selected view's bottom right corner
  103.       *
  104.       */
  105.     public static final int BOTTOM_RIGHT_POSITION = 3;
  106.  
  107.     static final int FIRST_POSITION    = TOP_LEFT_POSITION;
  108.     static final int LAST_POSITION     = BOTTOM_RIGHT_POSITION;
  109.  
  110.     static final int arrowXOffset = 0;
  111.     static final int arrowYOffset = 0;
  112.  
  113.     /** Constructs an Application. The Application will not begin processing
  114.       * Events until it receives a <b>run()</b> message.  You will almost never
  115.       * create an Application instance - the IFC machinery will create
  116.       * one for you.
  117.       * @see #run
  118.       */
  119.     public Application() {
  120.         super();
  121.         FoundationApplet ifcApplet;
  122.  
  123.         groupToApplication.put(Thread.currentThread().getThreadGroup(), this);
  124.         ifcApplet = FoundationApplet.applet();
  125.         if (ifcApplet == null) {
  126.             ifcApplet = createApplet();
  127.             ifcApplet.setApplication(this);
  128.             didCreateApplet = true;
  129.         } else {
  130.             isPaused = true;
  131.             ifcApplet.setupCanvas(this);
  132.         }
  133.         applet = ifcApplet;
  134.         _appResources = new AppletResources(this, codeBase());
  135.         timerQueue = new TimerQueue();
  136.         eventLoop.application = this;
  137.     }
  138.  
  139.     public static String releaseName()  {
  140.         return _releaseName;
  141.     }
  142.  
  143.     /** Constructs an Application for an existing Applet.  You should only
  144.       * use this constructor when including IFC Views within an AWT-based
  145.       * component hierarchy.  You must call <b>run()</b> on the returned
  146.       * Application for your Views to draw and receive events.
  147.       * @see FoundationPanel
  148.       * @see #run
  149.       */
  150.     public Application(java.applet.Applet applet) {
  151.         groupToApplication.put(Thread.currentThread().getThreadGroup(), this);
  152.         this.applet = applet;
  153.         _appResources = new AppletResources(this, codeBase());
  154.         timerQueue = new TimerQueue();
  155.         appletStarted();
  156.         eventLoop.application = this;
  157.     }
  158.  
  159.     /** Returns the application instance. */
  160.     public static Application application() {
  161.         ThreadGroup group = Thread.currentThread().getThreadGroup();
  162.         Application app = (Application)groupToApplication.get(group);
  163.  
  164.         if(app == null) {   /// ddk - ADDED FOR JAVASCRIPT SUPPORT
  165.             app = FoundationApplet.currentApplication();
  166.         }
  167.  
  168.         /*
  169.         if (app == null) {
  170.             System.err.println("Not in Application thread group!");
  171.             System.err.println("Thread: " + Thread.currentThread());
  172.             Thread.currentThread().getThreadGroup().list();
  173.             Thread.currentThread().getThreadGroup().stop();
  174.             Thread.dumpStack();
  175.         }
  176.         */
  177.         return app;
  178.     }
  179.  
  180.     /** Initializes the Application.  Override to perform any initialization.
  181.       */
  182.     public void init() {
  183.     }
  184.  
  185.     /** Called when the run loop has exited.
  186.      */
  187.     public void cleanup() {
  188.         Enumeration groups = groupToApplication.keys();
  189.         int iView;
  190.  
  191.         applicationDidStop();
  192.  
  193.         /* This will not destroy the applet. This will remove the application
  194.          * and thread groups
  195.          */
  196.         if (applet instanceof FoundationApplet) {
  197.             ((FoundationApplet)applet).cleanup();
  198.         }
  199.  
  200.         if (didCreateApplet) {
  201.             ((FoundationApplet)applet).destroyFromIFC();
  202.             if(applet.getParent() != null)
  203.                 applet.getParent().remove(applet);
  204.         }
  205.  
  206.         while (groups.hasMoreElements()) {
  207.             ThreadGroup group = (ThreadGroup)groups.nextElement();
  208.             Application app = (Application)groupToApplication.get(group);
  209.  
  210.             if (app == this) {
  211.                 groupToApplication.remove(group);
  212.                 break;
  213.             }
  214.         }
  215.  
  216.         timerQueue = null;
  217.         observers.removeAllElements();
  218.         eventLoop.application = null;
  219.     }
  220.  
  221.     /** Destroys the Application, including all ExternalWindows.
  222.       * Stops the EventLoop and causes <b>run()</b> to return.
  223.       */
  224.     public void stopRunning() {
  225.         eventLoop.stopRunning();
  226.     }
  227.  
  228.     /** This method is called in the AWT thread. It will
  229.      *  stop the event loop and wait until run is done
  230.      */
  231.     void stopRunningForAWT() {
  232.         synchronized( cleanupLock ) {
  233.             eventLoop.stopRunning();
  234.             while( true ) {
  235.                 try {
  236.                     cleanupLock.wait();
  237.                     return;
  238.                 } catch ( java.lang.InterruptedException e ) {
  239.                     /** ALERT what should we do? Can this really happen? */
  240.                 }
  241.             }
  242.         }
  243.     }
  244.  
  245.     /** Starts the Application's EventLoop.
  246.       */
  247.     public void run() {
  248.         // Having init() called here is kind of strange.  ALERT!
  249.         cleanupLock = new Object();
  250.         applicationDidStart();
  251.         init();
  252.         eventLoop.run();
  253.         cleanup();
  254.         /* Unblock the awt thread if necessary */
  255.         synchronized( cleanupLock ) {
  256.             cleanupLock.notify();
  257.         }
  258.     }
  259.  
  260. /* attributes */
  261.  
  262.     /** Returns the Application's EventLoop.
  263.       * @see EventLoop
  264.       */
  265.     public EventLoop eventLoop() {
  266.         return eventLoop;
  267.     }
  268.  
  269.     /**
  270.      * Returns a Vector containing the user's language preferences,
  271.      * specified by Strings, in order of preference.  For example, for a user
  272.      * preferring French over English, the Vector would contain the
  273.      * Strings "French" and "English," in that order.<br><br>
  274.      * <i><b>Note:</b> This method will return an empty Vector until such time
  275.      * as Applets can save user preferences to their local machine.  If you
  276.      * maintain user preferences at your site, you can request this
  277.      * Vector and fill it with the appropriate strings.</I>
  278.      */
  279.     Vector languagePreferences() {
  280.         if (_languageVector == null) {
  281.             _languageVector = new Vector();
  282.         }
  283.  
  284.         return _languageVector;
  285.     }
  286.  
  287. /* actions */
  288.  
  289.     /*
  290.      * Convenience method for forcing all pending drawing requests to appear
  291.      * onscreen immediately.  Equivalent to
  292.      * <pre>
  293.      *     getToolkit().sync();
  294.      * </pre>
  295.      * <I><b>Note:</b> Due to bugs in the getToolkit().sync() native
  296.      * implementation, this method currently does nothing.</i>
  297.      */
  298.     void syncGraphics() {
  299.   /* awt */
  300. // hangs the system sometimes with "unexpected asynch reply" messages
  301. //      getToolkit().sync();
  302.     }
  303.  
  304.  
  305.  
  306. /* interfaces */
  307.  
  308.     /*
  309.      * Returns a java.io.InputStream containing the Velocity interface file
  310.      * named
  311.      * <b>interfaceName</b>.  Uses the Application's AppletResources
  312.      * instance to
  313.      * retrieve the correct localized interface file based on the user's
  314.      * language preferences.
  315.      * @see AppletResources
  316.      */
  317.     java.io.InputStream streamForInterface(String interfaceName) {
  318.         return _appResources.streamForInterface(interfaceName);
  319.     }
  320.  
  321. /* other resources */
  322.  
  323.     /** Returns a java.io.InputStream containing data for the file named
  324.       * <b>resourceName</b> of type <b>type</b> (i.e. resides within a
  325.       * directory named "<b>type</b>" at the same directory level as the
  326.       * application's index.html file).
  327.       */
  328.     java.io.InputStream streamForResourceOfType(String resourceName,
  329.                                                        String type) {
  330.         return _appResources.streamForResourceOfType(resourceName, type);
  331.     }
  332.  
  333.     /** Returns <b>true</b> if the Application was started as an Applet. */
  334.     public boolean isApplet() {
  335.         return !didCreateApplet;
  336.     }
  337.  
  338.     java.io.InputStream streamForRelativePath(String relativePath) {
  339.   /* awt */
  340.         URL                     documentURL;
  341.         java.io.InputStream     inputStream;
  342.  
  343.         try {
  344.             documentURL = new URL(codeBase(), relativePath);
  345.         } catch (Exception e) {
  346.             System.err.println("Application.streamForRelativePath() - " + e);
  347.             documentURL = null;
  348.         }
  349.  
  350.         if (documentURL == null) {
  351.             return null;
  352.         }
  353.  
  354.         try {
  355.             inputStream = documentURL.openStream();
  356.         } catch (Exception e) {
  357.             System.err.println(
  358.                 "Application.streamForURL() - Trouble retrieving URL " +
  359.                 documentURL + " : " + e);
  360.             inputStream = null;
  361.         }
  362.  
  363.         return inputStream;
  364.     }
  365.  
  366.     AppletContext getAppletContext() {
  367.         return applet == null ? null : applet.getAppletContext();
  368.     }
  369.  
  370.     /** Returns the Application's codebase, the URL where the Applet's class
  371.       * file originated.
  372.       */
  373.     public URL codeBase() {
  374.         return applet.getCodeBase();
  375.     }
  376.  
  377.     /** Returns the values for the parameter <b>name</b>.
  378.       */
  379.     public String parameterNamed(String name) {
  380.         return applet == null ? null : applet.getParameter(name);
  381.     }
  382.  
  383.     /** Returns the Application's "main" RootView.  In the case of an Applet,
  384.       * this method returns the RootView associated with the Applet.  If
  385.       * not an Applet and never set by the Application, this method returns
  386.       * <b>null</b>.
  387.       * @see #setMainRootView
  388.       */
  389.     public RootView mainRootView() {
  390.         return mainRootView;
  391.     }
  392.  
  393.     /** Sets the main RootView.
  394.       * @see #mainRootView
  395.       */
  396.     public void setMainRootView(RootView view) {
  397.         addRootView(view);
  398.         mainRootView = view;
  399.     }
  400.  
  401.     /** Returns the Applet's RootView, or the RootView of the ExternalWindow
  402.       * that was most recently made the top-most window.
  403.       */
  404.     RootView firstRootView() {
  405.         return (RootView)rootViews.lastElement();
  406.     }
  407.  
  408.     /** Returns a Vector containing all the RootViews being displayed
  409.       * by the Application.  This vector is for <i>reading only</i> - do not
  410.       * modify.
  411.       */
  412.     public Vector rootViews() {
  413.         return rootViews;
  414.     }
  415.  
  416.     /** Returns a Vector containing all the ExternalWindows being displayed
  417.       * by the Application.  This Vector is for <i>reading only</i> - do not
  418.       * modify.
  419.       */
  420.     public Vector externalWindows() {
  421.         int i, count = rootViews.count();
  422.         Vector windows = new Vector();
  423.  
  424.         for (i = 0; i < count; i++) {
  425.             RootView rootView = (RootView)rootViews.elementAt(i);
  426.             ExternalWindow window = rootView.externalWindow();
  427.  
  428.             if (window != null) {
  429.                 windows.addElement(window);
  430.             }
  431.         }
  432.         return windows;
  433.     }
  434.  
  435.     /* We are playing this add/remove game to make sure that
  436.      * we don't add the same rootview twice and that the
  437.      * firstRootView is not changed while adding a new rootview.
  438.      */
  439.     void addRootView(RootView view) {
  440.         if (!rootViews.contains(view)) {
  441.             rootViews.insertElementAt(view, 0);
  442.             view.setApplication(this);
  443.         }
  444.     }
  445.  
  446.     void removeRootView(RootView view) {
  447.         rootViews.removeElement(view);
  448.         view.setApplication(null);
  449.         if(rootViews.count() > 0)
  450.             ((RootView)rootViews.lastElement()).didBecomeFirstRootView();
  451.     }
  452.  
  453.     void makeFirstRootView(RootView view) {
  454.         RootView wasFirstRootView;
  455.  
  456.         if(rootViews.indexOf(view) == -1)
  457.             return;
  458.  
  459.         if(rootViews.lastElement() != view) {
  460.             wasFirstRootView = (RootView)rootViews.lastElement();
  461.             rootViews.removeElement(view);
  462.             rootViews.addElement(view);
  463.             if(wasFirstRootView != null)
  464.                 wasFirstRootView.didResignFirstRootView();
  465.             view.didBecomeFirstRootView();
  466.         }
  467.     }
  468.  
  469.     java.awt.Frame frame() {
  470.         java.awt.Component comp;
  471.         for (comp = applet;
  472.              comp != null && !(comp instanceof java.awt.Frame);
  473.              comp = comp.getParent());
  474.         if (comp != null)
  475.             return (java.awt.Frame) comp;
  476.         else
  477.             return null;
  478.     }
  479.  
  480.     synchronized java.awt.MediaTracker mediaTracker() {
  481.         if (tracker == null) {
  482.             tracker = new java.awt.MediaTracker(applet);
  483.         }
  484.  
  485.         return tracker;
  486.     }
  487.  
  488.     synchronized int nextBitmapNumber() {
  489.         return bitmapCount++;
  490.     }
  491.  
  492.     synchronized TimerQueue timerQueue() {
  493.         return timerQueue;
  494.     }
  495.  
  496.     /** Registers <b>menuView</b> for notification to receive
  497.       * <b>mouseWillDown</b> messages.  The registered MenuView must be
  498.       * top-level (i.e. it has a null owner).
  499.       */
  500.     void addActiveMenuView(MenuView menuView) {
  501.         activeMenuViews.addElementIfAbsent(menuView);
  502.     }
  503.  
  504.     /** Unregisters <b>menuView</b> for <b>mouseWillDown</b> notification.
  505.       * @see #addActiveMenuView
  506.       */
  507.     void removeActiveMenuView(MenuView menuView) {
  508.         activeMenuViews.removeElement(menuView);
  509.     }
  510.  
  511.     /** Called before the EventLoop processes <b>anEvent</b>.
  512.       * The default implementation does nothing.
  513.       */
  514.     public void willProcessEvent(Event anEvent) {
  515.     }
  516.  
  517.     /** Called before the EventLoop processes <b>anEvent</b>, but before
  518.       * <b>willProcessEvent</b> is called.
  519.       */
  520.     void willProcessInternalEvent(Event anEvent) {
  521.         MenuView   menuView;
  522.         int        i;
  523.  
  524.         if (activeMenuViews.count() == 0 ||
  525.                 anEvent.type() != MouseEvent.MOUSE_DOWN) {
  526.             return;
  527.         }
  528.  
  529.         menuView = (MenuView)activeMenuViews.lastElement();
  530.         ((MenuView)menuView).mouseWillDown((MouseEvent)anEvent);
  531.     }
  532.  
  533.     /** Called after the EventLoop has processed <b>anEvent</b>.
  534.       * The default implementation does nothing.
  535.       */
  536.     public void didProcessEvent(Event anEvent) {
  537.     }
  538.  
  539.     /** Called after the EventLoop has processed <b>anEvent</b>, and after
  540.       * <b>didProcessEvent()</b> is called.
  541.       */
  542.     void didProcessInternalEvent(Event anEvent) {
  543.         drawAllDirtyViews();
  544.     }
  545.  
  546.     /** This method is called by InternalWindow and ExternalWindow
  547.      *  in showModally before waiting for an event.
  548.      * @private
  549.      */
  550.     protected void drawAllDirtyViews() {
  551.         int i, count;
  552.         RootView rView;
  553.  
  554.         count = rootViews.count();
  555.         for (i = 0; i < count; i++) {
  556.             rView = (RootView)rootViews.elementAt(i);
  557.             rView.drawDirtyViews();
  558.             rView._updateCursorAndMoveView();
  559.         }
  560.     }
  561.  
  562.     boolean isMac() {
  563.         String          osName;
  564.  
  565.         osName = System.getProperty("os.name");
  566.         if (osName != null && osName.startsWith("Mac")) {
  567.             return true;
  568.         }
  569.         return false;
  570.     }
  571.  
  572.     /** Subclassers can override to catch key down Events when there's no
  573.       * focused view. The default implementation handle keyboard UI.
  574.       * always call super if your application subclass is not processing
  575.       * the event.
  576.       */
  577.     public void keyDown(KeyEvent event) {
  578.         boolean processed = false;
  579.         RootView frv = firstRootView();
  580.         if(frv != null)
  581.             processed = frv.processKeyboardEvent(event,false);
  582.  
  583.         if(!processed) {
  584.             // Beep?
  585.         }
  586.     }
  587.  
  588.     /** Subclassers can override to catch key typed Events when there's no
  589.       * focused view.The default implementation handle keyboard UI.
  590.       * always call super if your application subclass is not processing
  591.       * the event. This method is called only when running with a 1.1 virtual machine
  592.       * and Application.application().handleExtendedKeyEvent
  593.       */
  594.     public void keyTyped(KeyEvent event) {
  595.     }
  596.  
  597.     /** Subclassers can override to catch key up Events when there's no
  598.       * focused view.The default implementation handle keyboard UI.
  599.       * always call super if your application subclass is not processing
  600.       * the event.
  601.       */
  602.     public void keyUp(KeyEvent event) {
  603.     }
  604.  
  605.  
  606.     /** Modal views
  607.       * @private
  608.       */
  609.     protected void beginModalSessionForView(View aView) {
  610.         RootView        rootView;
  611.  
  612.         if (aView == null)
  613.             throw new InconsistencyException(
  614.                             "beginModalSessionForView called with null view");
  615.  
  616.         _modalVector.addElement(aView);
  617.  
  618.         rootView = aView.rootView();
  619.         if (rootView != null) {
  620.             rootView.updateCursor();
  621.         }
  622.     }
  623.  
  624.     /** @private */
  625.     protected void endModalSessionForView(View aView) {
  626.         RootView        rootView;
  627.  
  628.         if( aView != _modalVector.lastElement()) {
  629.             throw new InconsistencyException("endModalSessionForView called for"+
  630.                                     " a view that is not the last modal view");
  631.         }
  632.  
  633.         _modalVector.removeLastElement();
  634.  
  635.         rootView = aView.rootView();
  636.         if (rootView != null) {
  637.             rootView.updateCursor();
  638.             rootView.validateSelectedView();
  639.         }
  640.     }
  641.  
  642.     /** Returns the top level view that has started a modal session.
  643.       */
  644.     public View modalView() {
  645.         if( _modalVector.count() > 0)
  646.             return (View) _modalVector.lastElement();
  647.         else
  648.             return null;
  649.     }
  650.  
  651.     boolean isModalViewShowing() {
  652.         if (_modalVector.count() == 0) {
  653.             return false;
  654.         } else {
  655.             return true;
  656.         }
  657.     }
  658.  
  659.     /** Causes <b>target</b> to receive a <b>performCommand()</b> message
  660.       * with <b>command</b> and <b>object</b>, from the Application's
  661.       * main thread using the Application's EventLoop.  This method will not
  662.       * return until the command has been performed. It can only be called
  663.       * from threads <i>other than</i> the main thread.
  664.       * @see #eventLoop
  665.       * @see EventLoop#mainThread
  666.       */
  667.     public void performCommandAndWait(Target target,
  668.                                       String command,
  669.                                       Object data) {
  670.         CommandEvent commandEvent = new CommandEvent(target, command, data);
  671.  
  672.         eventLoop.addEventAndWait(commandEvent);
  673.     }
  674.  
  675.     /** Causes <b>target</b> to receive a <b>performCommand()</b> message
  676.       * with <b>command</b> and <b>object</b>, after the current Event and
  677.       * others in the Application's EventLoop have been processed. If
  678.       * <b>ignorePrevious</b> is <b>true</b>, this method disposes of all
  679.       * pending requests with the same <b>target</b> and <b>command</b>.
  680.       * @see #eventLoop
  681.       */
  682.     public void performCommandLater(Target target,
  683.                                     String command,
  684.                                     Object data, boolean ignorePrevious) {
  685.         CommandEvent commandEvent = new CommandEvent(target, command, data);
  686.  
  687.         if (ignorePrevious) {
  688.             eventLoop.filterEvents(
  689.                 new CommandFilter(commandEvent.target, commandEvent.command,
  690.                                   data));
  691.         }
  692.         eventLoop.addEvent(commandEvent);
  693.     }
  694.  
  695.     /** Causes <b>target</b> to receive a <b>performCommand()</b> message
  696.       * with <b>command</b> and <b>object</b>, after the current Event and
  697.       * others in the Application's EventLoop have been processed.
  698.       * Equivalent to the code:
  699.       * <pre>
  700.       *     performCommandLater(target, command, data, false)
  701.       * </pre>
  702.       * @see #performCommandLater(Target, String, Object, boolean)
  703.       */
  704.     public void performCommandLater(Target target, String command,
  705.                                     Object data) {
  706.         performCommandLater(target, command, data, false);
  707.     }
  708.  
  709.     /** Creates and returns the Application's Applet.
  710.       * This method will only be called in a stand-alone application.
  711.       * Application subclasses can override this method to provide a
  712.       * custom subclass of FoundationApplet. This Applet must have been
  713.       * added to a java.awt.Frame and have an AppletStub.
  714.       * @see FoundationApplet
  715.       * @see java.awt.Frame
  716.       * @see java.applet.AppletStub
  717.       */
  718.     protected FoundationApplet createApplet() {
  719.         java.awt.Frame awtFrame = new java.awt.Frame();
  720.         FoundationApplet applet = new FoundationApplet();
  721.  
  722.         awtFrame.add(applet);
  723.         awtFrame.addNotify();
  724.         applet.addNotify();
  725.         applet.setStub(new FoundationAppletStub());
  726.         return applet;
  727.     }
  728.  
  729.     String exceptionHeader() {
  730.         return "Uncaught exception.  IFC release: " + _releaseName;
  731.     }
  732.  
  733.     /** Adds <b>observer</b> as an object that will receive notifications
  734.       * when the Application's running state changes. An observer
  735.       * might be interested in learning when the HTML page containing the
  736.       * Application is no longer visible, for example.  See the
  737.       * ApplicationObserver interface for more information.
  738.       * @see ApplicationObserver
  739.       * @see #removeObserver
  740.       */
  741.     public void addObserver(ApplicationObserver observer) {
  742.         observers.addElementIfAbsent(observer);
  743.     }
  744.  
  745.     /** Removes <b>observer</b> from the group of objects interested in
  746.       * notifications of changes in the Application's running state.
  747.       * @see #addObserver
  748.       */
  749.     public void removeObserver(ApplicationObserver observer) {
  750.         observers.removeElement(observer);
  751.     }
  752.  
  753.     /** Called from an Applet's <b>start()</b> method.  Notifies all
  754.       * ApplicationObservers that the Application has resumed.
  755.       * Normally called from FoundationApplet.
  756.       */
  757.     public void appletStarted() {
  758.         int i = observers.count();
  759.  
  760.         isPaused = false;
  761.         while (i-- > 0) {
  762.             ApplicationObserver observer;
  763.  
  764.             observer = (ApplicationObserver)observers.elementAt(i);
  765.             observer.applicationDidResume(this);
  766.         }
  767.         i = rootViews.count();
  768.         while (i-- > 0) {
  769.             RootView rootView = (RootView)rootViews.elementAt(i);
  770.  
  771.             if (rootView.externalWindow() == null) {
  772.                 rootView.setVisible(true);
  773.             }
  774.         }
  775.     }
  776.  
  777.     /** Called from an Applet's <b>stop()</b> method.  Notifies all
  778.       * ApplicationObservers that the Application has paused.
  779.       * Normally called from FoundationApplet.
  780.       */
  781.     public void appletStopped() {
  782.         int i = observers.count();
  783.  
  784.         isPaused = true;
  785.         while (i-- > 0) {
  786.             ApplicationObserver observer;
  787.  
  788.             observer = (ApplicationObserver)observers.elementAt(i);
  789.             observer.applicationDidPause(this);
  790.         }
  791.         i = rootViews.count();
  792.         while (i-- > 0) {
  793.             RootView rootView = (RootView)rootViews.elementAt(i);
  794.  
  795.             if (rootView.externalWindow() == null) {
  796.                 rootView.setVisible(false);
  797.             }
  798.         }
  799.     }
  800.  
  801.     void applicationDidStart() {
  802.         int i = observers.count();
  803.  
  804.         while (i-- > 0) {
  805.             ApplicationObserver observer;
  806.  
  807.             observer = (ApplicationObserver)observers.elementAt(i);
  808.             observer.applicationDidStart(this);
  809.         }
  810.     }
  811.  
  812.     void applicationDidStop() {
  813.         int i = observers.count();
  814.  
  815.         while (i-- > 0) {
  816.             ApplicationObserver observer;
  817.  
  818.             observer = (ApplicationObserver)observers.elementAt(i);
  819.             observer.applicationDidStop(this);
  820.         }
  821.     }
  822.  
  823.     /** Processes Application-specific Events. */
  824.     public void processEvent(Event event) {
  825.         if (event instanceof ApplicationEvent) {
  826.             if (event.type == ApplicationEvent.APPLET_STOPPED) {
  827.                 appletStopped();
  828.             } else if (event.type == ApplicationEvent.APPLET_STARTED) {
  829.                 appletStarted();
  830.             }
  831.         }
  832.     }
  833.  
  834.     /** Returns <b>true</b> if the Application's EventLoop is
  835.       * currently running.
  836.       */
  837.     public boolean isRunning() {
  838.         return eventLoop.isRunning();
  839.     }
  840.  
  841.     /** Returns <b>true</b> if the Application is currently paused.
  842.       * Applications pause when their Applet's HTML page becomes hidden.
  843.       */
  844.     public boolean isPaused() {
  845.         return isPaused;
  846.     }
  847.  
  848.     static Clipboard clipboard() {
  849.         synchronized (clipboardLock) {
  850.             if (clipboard == null) {
  851.                 clipboard = JDK11AirLock.clipboard();
  852.                 if (clipboard == null) {
  853.                     clipboard = new TextBag();
  854.                 }
  855.             }
  856.             return clipboard;
  857.         }
  858.     }
  859.  
  860.     /** Makes <b>aWindow</b> the current document for the application.
  861.       * <b>aWindow</b> should contain a document: isDocument should
  862.       * return true. If <b>aWindow</b> is null, the application will
  863.       * not have a current document.
  864.       *
  865.       */
  866.     public void makeCurrentDocumentWindow(Window aWindow) {
  867.         int i;
  868.         ApplicationObserver observer;
  869.  
  870.         if(aWindow != null && !(aWindow.containsDocument())) {
  871.             throw new InconsistencyException("makeCurrentDocumentWindow: window is not a document");
  872.         }
  873.  
  874.         if( currentDocumentWindow != null ) {
  875.             currentDocumentWindow.didResignCurrentDocument();
  876.             currentDocumentWindow = null;
  877.         }
  878.  
  879.         if(aWindow != null) {
  880.             currentDocumentWindow = aWindow;
  881.             currentDocumentWindow.didBecomeCurrentDocument();
  882.         }
  883.  
  884.         i = observers.count();
  885.         while (i-- > 0) {
  886.             observer = (ApplicationObserver)observers.elementAt(i);
  887.             try {
  888.                 /* New API for 1.1 */
  889.                 observer.currentDocumentDidChange(this,
  890.                              currentDocumentWindow);
  891.             } catch (IncompatibleClassChangeError e) {
  892.             }
  893.         }
  894.     }
  895.  
  896.     /** Return the current document window. Return null if there is no
  897.      *  current document window
  898.      */
  899.     public Window currentDocumentWindow() {
  900.         return currentDocumentWindow;
  901.     }
  902.  
  903.     /** Find and make a new window the current document.
  904.       * The new window should be different than <b>aWindow</b>
  905.       * If <b>aWindow</b> is an external window, try to find an external
  906.       * window that contains a document. If not found, try an internal window
  907.       * in the main root view that contains a document.
  908.       * If <b>aWindow</b> is an internal window, try to find an internal
  909.       * window in the same root view that contains a document.
  910.       * If <b>aWindow</b> is null, try to find an external window that
  911.       * contains a document and then an internal window in the main
  912.       * root view that contains a document.
  913.       * Override this method if your application needs a better strategy
  914.       * to find a new document window. This method is called when a window
  915.       * containing a document is closed.
  916.       *
  917.       */
  918.     public void chooseNextCurrentDocumentWindow(Window aWindow) {
  919.         Window nextWindow = null;
  920.         if(aWindow == null || aWindow instanceof ExternalWindow) {
  921.             nextWindow = _chooseNextExternalWindowWithDocument((ExternalWindow)aWindow);
  922.             if( nextWindow == null &&  mainRootView() != null )
  923.                 nextWindow = _chooseNextInternalWindowWithDocument(mainRootView(),null);
  924.         } else { // Internal Window
  925.             if(((InternalWindow)aWindow).rootView() != null)
  926.                 nextWindow = _chooseNextInternalWindowWithDocument(
  927.                                     ((InternalWindow)aWindow).rootView(),
  928.                                     (InternalWindow)aWindow);
  929.         }
  930.         makeCurrentDocumentWindow(nextWindow);
  931.     }
  932.  
  933.     /** Find the next internal window in aRootView that contains a document **/
  934.     Window _chooseNextInternalWindowWithDocument(RootView aRootView,InternalWindow win) {
  935.         Vector vector = aRootView.internalWindows();
  936.         InternalWindow w;
  937.         int i;
  938.         for(i = vector.count()-1 ; i >= 0 ; i--){
  939.             w = (InternalWindow) vector.elementAt(i);
  940.             if(w == win)
  941.                 continue;
  942.             else if(w.containsDocument())
  943.                 return w;
  944.         }
  945.         return null;
  946.     }
  947.  
  948.     Window _chooseNextExternalWindowWithDocument(ExternalWindow win) {
  949.         Vector vector = externalWindows();
  950.         ExternalWindow w;
  951.         int i;
  952.         for(i = vector.count()-1 ; i >= 0 ; i--) {
  953.             w = (ExternalWindow) vector.elementAt(i);
  954.             if(w == win)
  955.                 continue;
  956.             else if(w.containsDocument())
  957.                 return w;
  958.         }
  959.         return null;
  960.     }
  961.  
  962.     void focusChanged(View newFocusedView) {
  963.         int i;
  964.         ApplicationObserver observer;
  965.  
  966.         i = observers.count();
  967.         while (i-- > 0) {
  968.             observer = (ApplicationObserver)observers.elementAt(i);
  969.             try {
  970.                 /* New API for 1.1 */
  971.                 observer.focusDidChange(this, newFocusedView);
  972.             } catch (IncompatibleClassChangeError e) {
  973.             }
  974.         }
  975.     }
  976.  
  977.  
  978.     /** Keyboard UI changes **/
  979.     KeyboardArrow keyboardArrow() {
  980.         if(keyboardArrow == null)
  981.             keyboardArrow = new KeyboardArrow();
  982.         return keyboardArrow;
  983.     }
  984.  
  985.     /** Return the arrow position that should be used to show that <b>aView</b>
  986.       * is the selected view. Override this method and return the direction you
  987.       * need The default implementation will make sure that the arrow is
  988.       * visible. Default implementation's return value can be:
  989.       * TOP_LEFT_POSITION, TOP_RIGHT_POSITION, BOTTOM_LEFT_POSITION or
  990.       * BOTTOM_RIGHT_POSITION
  991.       *
  992.       */
  993.     public int keyboardArrowPosition(View aView) {
  994.         RootView rv = aView.rootView();
  995.         Rect rvBounds = rv.localBounds();
  996.         Rect  vBounds;
  997.         Image  image;
  998.         Point  hotSpot;
  999.         Point  extr = new Point();
  1000.         int i;
  1001.  
  1002.         vBounds = new Rect();
  1003.         aView.computeVisibleRect(vBounds);
  1004.         vBounds.intersectWith(aView.keyboardRect());
  1005.         aView.convertRectToView(rv,vBounds,vBounds);
  1006.  
  1007.         for(i=FIRST_POSITION ; i <= LAST_POSITION ; i++) {
  1008.             image = keyboardArrowImage(i);
  1009.             hotSpot = keyboardArrowHotSpot(i);
  1010.             extr    = keyboardArrowLocation(aView,i);
  1011.             extr.x -= hotSpot.x;
  1012.             extr.y -= hotSpot.y;
  1013.             if(rvBounds.contains(new Rect(extr.x,extr.y,image.width(),image.height())))
  1014.                 return i;
  1015.         }
  1016.  
  1017.         // Edge case, no image fit. Return the first one
  1018.         return FIRST_POSITION;
  1019.     }
  1020.  
  1021.     /** Return the arrow image that should be used for the position
  1022.       * <b>position</b>
  1023.       *
  1024.       */
  1025.     public Image keyboardArrowImage(int position) {
  1026.         switch(position) {
  1027.         case TOP_LEFT_POSITION:
  1028.             return Bitmap.bitmapNamed("netscape/application/topLeftArrow.gif");
  1029.         case TOP_RIGHT_POSITION:
  1030.             return Bitmap.bitmapNamed("netscape/application/topRightArrow.gif");
  1031.         case BOTTOM_RIGHT_POSITION:
  1032.             return Bitmap.bitmapNamed("netscape/application/bottomRightArrow.gif");
  1033.         case BOTTOM_LEFT_POSITION:
  1034.             return Bitmap.bitmapNamed("netscape/application/bottomLeftArrow.gif");
  1035.         default:
  1036.             return null;
  1037.         }
  1038.     }
  1039.  
  1040.     /** Return the Point that should be used for the the keyboard arrow
  1041.       * image for the position <b>position</b>
  1042.       *
  1043.       */
  1044.     public Point keyboardArrowHotSpot(int position) {
  1045.         switch(position) {
  1046.         case TOP_LEFT_POSITION:
  1047.             return new Point(8,12);
  1048.         case TOP_RIGHT_POSITION:
  1049.             return new Point(0,12);
  1050.         case BOTTOM_RIGHT_POSITION:
  1051.             return new Point(0,0);
  1052.         case BOTTOM_LEFT_POSITION:
  1053.             return new Point(8,0);
  1054.         default:
  1055.             return null;
  1056.         }
  1057.     }
  1058.  
  1059.     /** Return the arrow location.The location is the point where
  1060.       * the arrow hot spot should be in <b>aView</b>'s root view
  1061.       * coordinate system.
  1062.       *
  1063.       */
  1064.     public Point keyboardArrowLocation(View aView,int position) {
  1065.         RootView rv = aView.rootView();
  1066.         Rect rvBounds = rv.localBounds();
  1067.         Rect b = aView.localBounds();
  1068.  
  1069.         b = new Rect();
  1070.         aView.computeVisibleRect(b);
  1071.         b.intersectWith(aView.keyboardRect());
  1072.  
  1073.         if(b.width == 0 || b.height == 0)
  1074.             return new Point(Integer.MAX_VALUE,Integer.MAX_VALUE);
  1075.  
  1076.         aView.convertRectToView(aView.rootView(),b,b);
  1077.         switch(position) {
  1078.         case TOP_LEFT_POSITION:
  1079.             return new Point(b.x + arrowXOffset,b.y + arrowYOffset);
  1080.         case TOP_RIGHT_POSITION:
  1081.             return new Point(b.x + b.width - arrowXOffset,
  1082.                              b.y + arrowYOffset);
  1083.         case BOTTOM_LEFT_POSITION:
  1084.             return new Point(b.x + arrowXOffset,
  1085.                              b.y + b.height - arrowYOffset);
  1086.         case BOTTOM_RIGHT_POSITION:
  1087.             return new Point(b.x + b.width - arrowXOffset,
  1088.                              b.y + b.height - arrowYOffset);
  1089.         default:
  1090.             throw new InconsistencyException("Unknown position " + position);
  1091.         }
  1092.     }
  1093.  
  1094.     /** Enable/Disable keyboard UI for this application. This method should
  1095.       * be called in the init method of Application. This flag is checked
  1096.       * once at startup, so changes to this switch will be ignored during the
  1097.       * lifetime of the application.
  1098.       *
  1099.       */
  1100.     public void setKeyboardUIEnabled(boolean aFlag) {
  1101.         _kbdUIEnabled=aFlag;
  1102.     }
  1103.  
  1104.     /** Return whether keyboard UI is enabled
  1105.       *
  1106.       */
  1107.     public boolean isKeyboardUIEnabled() {
  1108.         return _kbdUIEnabled;
  1109.     }
  1110.  
  1111.     /** @private */
  1112.     public java.applet.Applet applet() {
  1113.         return applet;
  1114.     }
  1115.  
  1116.     /** @private */
  1117.     public AppletResources appletResources() {
  1118.         return _appResources;
  1119.     }
  1120.  
  1121.     /** Returns the "current" text string.  This interacts with the
  1122.       * the native clipboard on platforms where it is available
  1123.       *
  1124.       */
  1125.     public static String clipboardText() {
  1126.         return clipboard().text();
  1127.     }
  1128.  
  1129.     /** Returns the "current" text string.  This interacts with the
  1130.       * the native clipboard on platforms where it is available
  1131.       *
  1132.       */
  1133.     public static void setClipboardText(String text) {
  1134.         clipboard().setText(text);
  1135.     }
  1136.  
  1137.     /**
  1138.       * The RootView will attempt to detect and eliminate extraneous
  1139.       * mouseDown events under certain circumstances. This should should
  1140.       * only affect VMs under JDK 1.1.x. JDK 1.0.2 VMs should be unaffected.
  1141.       * By default this process is turned on.
  1142.       */
  1143.     public void setHandleJDK11MouseEvents(boolean b)    {
  1144.         jdkMouseEventHackEnabled = b;
  1145.     }
  1146.  
  1147.     public boolean handleJDK11MouseEvents() {
  1148.         return jdkMouseEventHackEnabled;
  1149.     }
  1150.  
  1151.  
  1152.     /** Override this method and return true if you want your application/applet
  1153.      *  to receive some ExtendedKeyEvent instead of KeyEvent. This will also enable
  1154.      *  keyTyped events. This works only when your application/applet is running with 1.1
  1155.      *  The default implementation returns false to garantee backward compatibility
  1156.      */
  1157.     public boolean handleExtendedKeyEvent() {
  1158.         return false;
  1159.     }
  1160.  
  1161.     /** IFC uses this method when it wants to find a class from a string
  1162.       * This method will ask the foundation applet to load the class to
  1163.       * use the applet class loader. If there is no foundation applet,
  1164.       * Class.forName will be used.
  1165.       * Override this method if you do not want to use a FoundationApplet
  1166.       * and provide a different way to load a class
  1167.       */
  1168.     public Class classForName(String aName) throws ClassNotFoundException {
  1169.       java.applet.Applet anApplet = applet();
  1170.       if(anApplet instanceof FoundationApplet)
  1171.         return ((FoundationApplet)anApplet).classForName(aName);
  1172.       else
  1173.         return Class.forName(aName);
  1174.     }
  1175. }
  1176.  
  1177.